package br.uefs.dexa.rfframework.gui.interfaces;

import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.RoundingMode;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import br.uefs.dexa.rfframework.gui.exceptions.MemoryShortageException;
import br.uefs.dexa.rfframework.gui.factory.GUIAbstractFactory;
import br.unicamp.ic.lis.rfframework.objects.DigitalObject;
import br.unicamp.ic.lis.rfframework.objects.DigitalObjectSet;
import br.unicamp.ic.lis.rfframework.objects.IDigitalObject;
import br.unicamp.ic.lis.rfframework.objects.exceptions.EmptyCollectionException;
import br.unicamp.ic.lis.rfframework.objects.exceptions.ObjectNotFoundException;

public abstract class OutputObjectSetContainerPanel extends ObjectSetContainerPanel implements RelevanceFeedbackViewer {


	private static final long serialVersionUID = -3946189043905977671L;
	
	public static final Color QUERY_SELECTION_COLOR = Color.GREEN;
	
	public static final Color RELEVANT_SELECTION_COLOR = Color.BLUE;
	
	
	protected OutputObjectSetContainerPanel(RFUserGUI gui) 
	{
		super(gui);
	}
	
	
	public void showObjects(DigitalObjectSet<DigitalObject> objects)
	{
		
		removeCurrentObjects();
		
		startLoadingProgressViewer(0, objects.getSize());
		
		for (int i = 0; i < objects.getSize(); i++) {
			
			IDigitalObject object;
			int progress = i;
			
			try {
				object = objects.getObjectByIndex(i);
			} catch (ObjectNotFoundException e) {
				e.printStackTrace(); return;
			} catch (EmptyCollectionException e) {
				e.printStackTrace(); return;
			}

			updateLoadingProgressViewer(progress);
							
			try {
				
				if (!SwingUtilities.isEventDispatchThread()) {
					EventQueue.invokeAndWait(new Runnable() {
						public void run() 
						{
							addObject(object);
						}
					});
				
				} else {
					addObject(object);
				}
				
				checkMemory();

			} catch (java.lang.OutOfMemoryError e) {
				
				JOptionPane.showMessageDialog(null, "Out of memory.\n Shuting down.","FATAL ERROR", JOptionPane.ERROR_MESSAGE);
				
				e.printStackTrace();
				System.exit(0);
				
			} catch (MemoryShortageException e) {
				
				int itensLeft = objects.getSize() - i;	
				JOptionPane.showMessageDialog(null, e.getMessage() + "\n"
						+ itensLeft + " itens were not shown.",
						"Memory shortage", JOptionPane.WARNING_MESSAGE);
				
				break;
				
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
		}
		
		finalizeLoadingProgressViewer();

	}
	
	
	@Override
	public void showObjectPanel(DigitalObjectPanel panel)
	{
		getOuputObjectsContainer().add(panel);
		getOuputObjectsContainer().revalidate();
		getOuputObjectsContainer().repaint();
	}
	
	@Override
	public void removeObjectPanel(DigitalObjectPanel panel)
	{
		getOuputObjectsContainer().remove(panel);
		getOuputObjectsContainer().revalidate();
		getOuputObjectsContainer().repaint();
	}
	
	@Override
	public  DigitalObjectPanel getDigitalObjectPanel(IDigitalObject object)
	{
		return GUIAbstractFactory.getInstance().getVisualizationObjectPanel(object);
	}

	@Override
	public void beforeQuerySelection()
	{
		//empty
	}

	@Override
	public void querySelectionMode()
	{
		for (DigitalObjectPanel panel : panels.values()) 
		{
			panel.resetBehavior();
			panel.setQuerySelectionBehavior(getGUI());

			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					panel.validate();
				}
			});
		}
	}
	
	@Override
	public void afterQuerySelection()
	{
		//empty
	}
	
	@Override
	public void beforeRelevanceFeedback()
	{
		//empty
	}
	
	@Override
	public void relevanceFeedbackMode()
	{
		for (DigitalObjectPanel panel : panels.values()) 
		{
			panel.resetBehavior();
			panel.setRelevantSelectionBehavior(getGUI());

			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					panel.validate();
				}
			});
		}
	}
	
	@Override
	public void afterRelevanceFeedback()
	{
		//empty
	}

	
	
	public void neutralMode()
	{
		for (DigitalObjectPanel panel : panels.values()) 
		{
			panel.resetBehavior();

			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					panel.validate();
				}
			});
		}
	}
	
	
	public void resetSelected(IDigitalObject object)
	{
		DigitalObjectPanel panel = panels.get(object.getId());
		panel.setSelected(false);
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				panel.setBackground(UIManager.getColor("control"));
				panel.revalidate();
				panel.repaint();
			}
		});
		
	}
	
	private void removeCurrentObjects()
	{
		panels.clear();
		clearObjects();
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				getOuputObjectsContainer().removeAll();
				getOuputObjectsContainer().revalidate();
				getOuputObjectsContainer().repaint();
			}
		});
		
	}
	
	
	private void checkMemory() throws MemoryShortageException
	{
		Runtime rt = Runtime.getRuntime();
		double freeMemoryPercentage = (double) rt.freeMemory() / (double) rt.totalMemory();
		if (freeMemoryPercentage < RFUserGUI.MINIMUM_FREE_MEMORY_PERCENTAGE)
			throw new MemoryShortageException("Avalilable memory: " + (round(freeMemoryPercentage * 100, 2)) + "%");
	}
	
	
	
	//thanks to the guy at http://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places
	private double round(double value, int places) 
	{
	    if (places < 0) throw new IllegalArgumentException();

	    BigDecimal bd = new BigDecimal(value);
	    bd = bd.setScale(places, RoundingMode.HALF_UP);
	    return bd.doubleValue();
	}
	
	
	
	
	/*
	 * Returns the GUI container responsible for keeping the output DigitalObjectPanels.
	 * As an example, can simply return a JPanel containing the output DigitalObjectPanels;
	 * Must be implemented on subclasses. 
	 */
	public abstract Container getOuputObjectsContainer();
	
	/*
	 * Start execution of some loading progress indicator for loading objects.
	 * As an example, can implement a progress bar. 
	 * This method is empty, so it can optionally be implemented on subclasses.
	 */
	public void startLoadingProgressViewer(int min, int max)
	{
		//empty
	}
	
	/*
	 * update loading progress indicator for loading objects,
	 * previously initiated on startLoadingProgressViewer() method. 
	 * This method is empty, so it can optionally be implemented on subclasses.
	 */
	public void updateLoadingProgressViewer(int progress)
	{
		//empty
	}
	
	/*
	 * Finalize execution of some loading progress indicator for loading objects, 
	 * previously initiated on startLoadingProgressViewer() method.
	 * This method is empty, so it can optionally be implemented on subclasses.
	 */
	public void finalizeLoadingProgressViewer()
	{
		//empty
	}
	

}
